using System;
using System.Collections.Generic;
using System.Text;
using MacomberMap.Common.Database_Interface.CIM;
using System.Windows.Forms;
using MacomberMap.Common.Internals;
using System.Xml;
using MacomberMap.Common.Database_Interface.CIM.CIM_Interface;
using MacomberMap.Common.Database_Interface.CIM.Components;
using System.Threading;
using System.IO;
using MacomberMap.Common.Database_Interface;

namespace MacomberMap.Loader.Export
{
    /// <summary>
    /// This class handles exporting of the trace for Outage Scheduler.
    /// </summary>
    public class MM_Exporter_OS: MM_Exporter
    {
        #region Variable Declarations

        /// <summary>
        /// Return the current progress
        /// </summary>
        public override float Progress
        {
            get { return Current / Total; }
        }
        /// <summary>
        /// The current step of export
        /// </summary>
        private float Current = 0;

        /// <summary>
        /// The total number of steps for export
        /// </summary>
        private float Total=0;

        /// <summary>
        /// The parameters for the CRR exporter - file name and CIM Db instance for retrieving traces
        /// </summary>
        private KeyValuePair<String, MM_Database_CIM> InParams;
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize the OS Exporter
        /// </summary>
        /// <param name="OutDirectory">The outgoing directory</param>
        /// <param name="CIMDb">The CIM database/repository</param>
        /// <param name="Parent">The list view in which this item will reside</param>
        public MM_Exporter_OS(String OutDirectory, MM_Database_CIM CIMDb, ListView Parent): base(Parent, "OS Exporter")
        {            
            this.InParams = new KeyValuePair<string, MM_Database_CIM>(OutDirectory, CIMDb);
            
            //Go through our XML configuration, and tally up all the items of the specified size
            foreach (XmlElement xElem in MM_Configuration.xConfig.SelectNodes("/MacomberMap_Loader/OutageScheduler/SeedElement"))
                this.Total += MM_Database_CIM.ElementsOfType(xElem.Attributes["value"].Value).Count;                       
        }


        /// <summary>
        /// A private class for comparing two CIM elements
        /// </summary>
        private class CIMComparer : IComparer<CIM_Element>
        {

            /// <summary>
            /// Compare two CIM elements
            /// </summary>
            /// <param name="x">The first element</param>
            /// <param name="y">The second element</param>
            /// <returns></returns>
            public int Compare(CIM_Element x, CIM_Element y)
            {
                String Substation1;
                if (x.Type == "cim:ACLineSegment" || x.Type == "cim:SeriesCompensator")
                    Substation1 = x.ACSegmentStations[0].Name + " to " + x.ACSegmentStations[1].Name;
                else if (x.Substation == null)
                    Substation1 = x.Nodes[0].Substation.Name;
                else
                    Substation1 = x.Substation.Name;

                String Substation2;
                if (y.Type == "cim:ACLineSegment" || y.Type == "cim:SeriesCompensator")
                    Substation2 = y.ACSegmentStations[0].Name + " to " + y.ACSegmentStations[1].Name;
                else if (y.Substation == null)
                    Substation2 = y.Nodes[0].Substation.Name;
                else
                    Substation2 = y.Substation.Name;

                int SubDiff = String.Compare(Substation1, Substation2, true);
                if (SubDiff != 0)
                    return SubDiff;

                //Now, compare element types
                
                int TypeDiff = String.Compare(x.Type.Substring(x.Type.IndexOf(':')+1),  y.Type.Substring(y.Type.IndexOf(':')+1));
                    
                    //Split(':')[1], y.Type.Split(':')[1]);
                if (TypeDiff != 0)
                    return TypeDiff;

                //Now, compare the element name types
                int NameDiff = String.Compare(x.Name, y.Name, true);
                if (NameDiff != 0)
                    return NameDiff;

                //If we have elements of the same name, substation & type, they must have different voltages
                if (x.VoltageLevel != null && y.VoltageLevel != null)
                {
                    int VoltageDiff = float.Parse(x.VoltageLevel.Name.Split(' ')[0]).CompareTo(float.Parse(y.VoltageLevel.Name.Split(' ')[0]));
                    if (VoltageDiff != 0)
                        return VoltageDiff;
                }
                return x.rdfID.CompareTo(y.rdfID);
                
            }
        }

       

        /// <summary>
        /// Initiate the outgoing OS export
        /// </summary>
        public override void ProcessExporter()
        {
            String FileName = InParams.Key.Replace("[ModelDirectory]", Path.GetDirectoryName(MM_Database_CIM.CIMFileName)) + "\\OS-Tracer-" + DateTime.Now.ToString("yyyy-MM-dd") + "-" + Path.GetFileNameWithoutExtension(MM_Database_CIM.CIMFileName);
            XmlElement BaseNode = MM_Configuration.xConfig.SelectSingleNode("/MacomberMap_Loader/OutageScheduler") as XmlElement;

            //Pull our list of types to be ignored
            List<String> IgnoreTypes = new List<string>();
            foreach (XmlElement xElem in BaseNode.SelectNodes("IgnoreOnTrace"))
                IgnoreTypes.Add(xElem.Attributes["value"].Value);

            //Track exported types
            SortedDictionary<String, int> ExportedTypes = new SortedDictionary<string, int>();
            SortedDictionary<String, int> ExportedRelated = new SortedDictionary<string, int>();

            Current = 0;
            DateTime StartTime = DateTime.Now;
            Encoding UTF8WithoutBOM = new UTF8Encoding(false);
            using (StreamWriter sW = new StreamWriter(FileName + "-Diagnostics.txt"))
            using (XmlTextWriter xW = new XmlTextWriter(FileName + ".xml", UTF8WithoutBOM))
            {
                xW.Formatting = Formatting.Indented;
                xW.WriteStartDocument();
                xW.WriteStartElement("ROWSET");
                Dictionary<CIM_Element, CIM_TraceList> OutDic;
                foreach (XmlElement xElem in BaseNode.SelectNodes("SeedElement"))
                    foreach (CIM_Element Elem in MM_Database_CIM.ElementsOfType(xElem.Attributes["value"].Value))
                    {
                        OutDic = CIM_Tracer.Tracer(Elem, true, true, null);
                        if (Elem.Substation != null)
                            sW.WriteLine(Elem.Type.Substring(4) + " " + Elem.Name + " (" + Elem.Substation.Name + ") " + Elem.TEID );
                        else if (Elem.ACSegmentStations.Length > 0 && Elem.Parse("Line") != null)
                            sW.WriteLine(Elem.Type.Substring(4) + " " + Elem.Name + "/ Line " + Elem.Parse("Line").Name + " (" + Elem.ACSegmentStations[0].Name + " to " + Elem.ACSegmentStations[1].Name + ") " + Elem.TEID);
                        else if (Elem.ACSegmentStations.Length > 0 && Elem.Parse("Compensator") != null)
                            sW.WriteLine(Elem.Type.Substring(4) + " " + Elem.Name + "/ ZBR " + Elem.Parse("Compensator").Name + " (" + Elem.ACSegmentStations[0].Name + " to " + Elem.ACSegmentStations[1].Name + ") " + Elem.TEID);
                        SortedDictionary<CIM_Element, CIM_TraceList> OutDic2 = new SortedDictionary<CIM_Element, CIM_TraceList>(OutDic, new CIMComparer());
                        foreach (CIM_Element Elem2 in OutDic2.Keys)
                            if (!IgnoreTypes.Contains(Elem2.Type))
                            {
                                xW.WriteStartElement("NMMS_RELATED_EQUIPMENT");
                                xW.WriteElementString("EQUIPMENT_IDENT", ExportElement(Elem, ExportedTypes).rdfID.ToString());                                
                                CIM_Element Exp2 = ExportElement(Elem2, ExportedRelated);
                                xW.WriteElementString("RELATED_EQUIPMENT_IDENT", Exp2.rdfID.ToString());
                                if (Exp2.Substation != null)
                                    sW.WriteLine("\t" + Exp2.Type.Substring(4) + " " + Exp2.Name + " (" + Exp2.Substation.Name + ") " + Exp2.TEID);
                                else if (Exp2.ACSegmentStations.Length > 0 && Exp2.Parse("Line") != null)
                                    sW.WriteLine("\t" + Exp2.Type.Substring(4) + " " + Exp2.Name + "/ Line " + Exp2.Parse("Line").Name + " (" + Exp2.ACSegmentStations[0].Name + " to " + Exp2.ACSegmentStations[1].Name + ") " + Exp2.TEID);
                                else if (Exp2.ACSegmentStations.Length > 0 && Exp2.Parse("Compensator") != null)
                                    sW.WriteLine("\t" + Exp2.Type.Substring(4) + " " + Exp2.Name + "/ ZBR " + Exp2.Parse("Compensator").Name + " (" + Exp2.ACSegmentStations[0].Name + " to " + Exp2.ACSegmentStations[1].Name + ") " + Exp2.TEID);
                                else
                                    sW.WriteLine("\t" + Exp2.Type.Substring(4) + " " + Exp2.Name + " " + Elem.TEID);
                                xW.WriteElementString("EFFECTIVE_DATE", DateTime.Now.ToString("yyyy:MM:dd"));
                                xW.WriteEndElement();
                            }
                        Current++;
                    }

                xW.WriteEndDocument();
                xW.Close();
                sW.Close();
            }

            //Write out the exported types
            using (StreamWriter sW = new StreamWriter(FileName + "-ExportedTypes.csv"))
            {
                sW.WriteLine("\"Category\",\"Type\",\"Count\"");
                foreach (KeyValuePair<string, int> kvp in ExportedTypes)
                    sW.WriteLine("\"{0}\",\"{1}\",{2}", "Seed", kvp.Key, kvp.Value);
                foreach (KeyValuePair<string, int> kvp in ExportedRelated)
                    sW.WriteLine("\"{0}\",\"{1}\",{2}", "Related", kvp.Key, kvp.Value);
                sW.Flush();
                sW.Close();
            }

            //Write out the flag file
            using (StreamWriter sW = new StreamWriter(FileName + ".flg"))
            {
                sW.WriteLine("Completed OS tracer export on " + DateTime.Now.ToString() + ", " + (DateTime.Now - StartTime) + ", run by " + Environment.UserDomainName + "\\" + Environment.UserName);
                sW.Close();
            }

            //Send out the notification of completed email
            StringBuilder OutString = new StringBuilder();
            OutString.AppendLine(String.Format("This is the Macomber Map Loader's OS Tracer Module, version {0}, running on {1}, by {2}\\{3} at {4}", Application.ProductVersion, Environment.MachineName, Environment.UserDomainName, Environment.UserName, DateTime.Now));
            OutString.AppendLine();
            OutString.AppendLine(String.Format("The OS tracer was ran against CIM file {0}, and the results have been saved into {1}, along with additional diagnostics.", MM_Database_CIM.CIMFileName, FileName + ".xml"));
            MM_Email_Sending.SendEmail(MM_Configuration.xConfig["MacomberMap_Loader"]["SMTPServer"].Attributes["Value"].Value, BaseNode.Attributes["FromEmail"].Value, BaseNode.Attributes["FromEmail"].Value,  BaseNode.Attributes["Subject"].Value, BaseNode.Attributes["MailRecipients"].Value.Split(','), BaseNode.Attributes["CarbonCopies"].Value.Split(','), BaseNode.Attributes["BlindCarbonCopies"].Value.Split(','), OutString.ToString());
                
        }

        /// <summary>
        /// Retrieve an export-appropriate CIM element
        /// </summary>
        /// <param name="InElement">The incoming CIM element</param>
        /// <param name="ElemTypes">The tally of exported element types</param>
        /// <returns></returns>
        private CIM_Element ExportElement(CIM_Element InElement, SortedDictionary<String, int> ElemTypes)                                
        {
            if (InElement.Type == "cim:Line")                                    
            {
            foreach (CIM_Element Elem in InElement.Links)
                if (Elem.Type == "cim:ACLineSegment" || Elem.Type == "cim:SeriesCompensator")
                {
                    if (ElemTypes.ContainsKey(Elem.Type))
                        ElemTypes[Elem.Type]++;
                    else
                        ElemTypes.Add(Elem.Type, 1);
                    return Elem;
                }
            }
            else if (InElement.Type == "cim:SynchronousMachine")
            {
                foreach (CIM_Element Elem in InElement.Links)
                    if (Elem.Type.EndsWith("Unit", StringComparison.CurrentCultureIgnoreCase))
                    {
                        if (ElemTypes.ContainsKey(Elem.Type))
                            ElemTypes[Elem.Type]++;
                        else
                            ElemTypes.Add(Elem.Type, 1);
                        return Elem;
                    }
            }
            if (ElemTypes.ContainsKey(InElement.Type))
                ElemTypes[InElement.Type]++;
            else
                ElemTypes.Add(InElement.Type, 1);
            return InElement;                   
        }
        #endregion


    }
}
